
<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

    <title>Vim Mode: Theory of Operation &#8212; Leo 6.7.2 documentation</title>
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
    <link rel="stylesheet" type="text/css" href="_static/classic.css" />
    <link rel="stylesheet" type="text/css" href="_static/custom.css" />
    
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
    <script src="_static/jquery.js"></script>
    <script src="_static/underscore.js"></script>
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
    <script src="_static/doctools.js"></script>
    <script src="_static/sphinx_highlight.js"></script>
    
    <script src="_static/sidebar.js"></script>
    
    <link rel="index" title="Index" href="genindex.html" />
    <link rel="search" title="Search" href="search.html" />
    <link rel="next" title="The Leonine World" href="leonine-world.html" />
    <link rel="prev" title="Exploring Leo’s Code Base" href="theory.html" /> 
  </head><body>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="leonine-world.html" title="The Leonine World"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="theory.html" title="Exploring Leo’s Code Base"
             accesskey="P">previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" accesskey="U">Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">Vim Mode: Theory of Operation</a></li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body" role="main">
            
  <section id="vim-mode-theory-of-operation">
<h1>Vim Mode: Theory of Operation<a class="headerlink" href="#vim-mode-theory-of-operation" title="Permalink to this heading">¶</a></h1>
<p>This is the theory of operation of Leo’s vim mode, contained in
leo/core/leoVim.py. It discusses everything you need to understand the code
and to add new vim commands.</p>
<div class="contents local topic" id="contents">
<p class="topic-title">Contents</p>
<ul class="simple">
<li><p><a class="reference internal" href="#the-big-picture" id="id1">The big picture</a></p></li>
<li><p><a class="reference internal" href="#a-simple-key-handler" id="id2">A simple key handler</a></p></li>
<li><p><a class="reference internal" href="#a-more-complex-key-handler" id="id3">A more complex key handler</a></p>
<ul>
<li><p><a class="reference internal" href="#vim-d" id="id4">vim_d</a></p></li>
<li><p><a class="reference internal" href="#vim-d2" id="id5">vim_d2</a></p></li>
<li><p><a class="reference internal" href="#vim-d3" id="id6">vim_d3</a></p></li>
<li><p><a class="reference internal" href="#vis-d" id="id7">vis_d</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#code-level-details" id="id8">Code level details</a></p>
<ul>
<li><p><a class="reference internal" href="#initialization" id="id9">Initialization</a></p></li>
<li><p><a class="reference internal" href="#dispatchers" id="id10">Dispatchers</a></p></li>
<li><p><a class="reference internal" href="#about-key-handlers" id="id11">About key handlers</a></p></li>
<li><p><a class="reference internal" href="#ivars-for-key-handlers" id="id12">Ivars for key handlers</a></p></li>
<li><p><a class="reference internal" href="#handling-tabs" id="id13">Handling tabs</a></p></li>
<li><p><a class="reference internal" href="#api-s-for-key-handlers" id="id14">API’s for key handlers</a></p></li>
<li><p><a class="reference internal" href="#vc-return-value-and-internal-error-checking" id="id15">vc.return_value and internal error checking</a></p></li>
</ul>
</li>
</ul>
</div>
<section id="the-big-picture">
<h2><a class="toc-backref" href="#id1">The big picture</a><a class="headerlink" href="#the-big-picture" title="Permalink to this heading">¶</a></h2>
<p>Leo’s vim mode dispatches keystrokes sent to it from k.masterKeyHandler to <strong>key handlers</strong>.</p>
<p>Each key handler handles the incoming key and then calls either vc.accept(), vc.done(), vc.ignore() or vc.quit(). These methods tell k.masterKeyHandler whether vim-mode has completely handled the key. If so, k.masterKeyHandler simply returns. Otherwise, k.masterKeyHandler handles the key as usual.</p>
</section>
<section id="a-simple-key-handler">
<h2><a class="toc-backref" href="#id2">A simple key handler</a><a class="headerlink" href="#a-simple-key-handler" title="Permalink to this heading">¶</a></h2>
<p>The handler for the G command moves or extends the cursor depending on vc.state:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">vim_G</span><span class="p">(</span><span class="n">vc</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&#39;&#39;&#39;Put the cursor on the last character of the body pane.&#39;&#39;&#39;</span>
    <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">is_text_widget</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">w</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">state</span> <span class="o">==</span> <span class="s1">&#39;visual&#39;</span><span class="p">:</span>
            <span class="n">vc</span><span class="o">.</span><span class="n">do</span><span class="p">(</span><span class="s1">&#39;end-of-buffer-extend-selection&#39;</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">vc</span><span class="o">.</span><span class="n">do</span><span class="p">(</span><span class="s1">&#39;end-of-buffer&#39;</span><span class="p">)</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">done</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
</pre></div>
</div>
<p>On entry, the dispatcher has set vc.w to the widget with focus. First, the code ensures that this widget is a text widget. If so, the code uses the vc.do method, a thin wrapper for c.k.simulateCommand, to execute Leo commands by name.</p>
</section>
<section id="a-more-complex-key-handler">
<h2><a class="toc-backref" href="#id3">A more complex key handler</a><a class="headerlink" href="#a-more-complex-key-handler" title="Permalink to this heading">¶</a></h2>
<p>The vc.vim_d method and its follow-on methods handle vim’s d commands.</p>
<p>The vc.vis_d method handles the d keystroke that ends visual mode.</p>
<p>The following sections examine each piece of the code in detail. If you understand how it works you should know everything you need to write any other key handler.</p>
<section id="vim-d">
<h3><a class="toc-backref" href="#id4">vim_d</a><a class="headerlink" href="#vim-d" title="Permalink to this heading">¶</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">vim_d</span><span class="p">(</span><span class="n">vc</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&#39;&#39;&#39;</span>
<span class="sd">    N dd      delete N lines</span>
<span class="sd">    d{motion} delete the text that is moved over with {motion}</span>
<span class="sd">    &#39;&#39;&#39;</span>
    <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">is_text_widget</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">w</span><span class="p">):</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">n</span> <span class="o">=</span> <span class="mi">1</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">accept</span><span class="p">(</span><span class="n">handler</span><span class="o">=</span><span class="n">vc</span><span class="o">.</span><span class="n">vim_d2</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
</pre></div>
</div>
<p>This is the key handler for the ‘d’ key in normal mode.</p>
<p>The entry in vc.normal_dispatch_d for ‘d’ is: ‘d’:vc.vim_d.</p>
<p>Because this command changes text, vc.is_text_widget(vc.w) must be True. If
so, this handler simply calls vc.accept(handler=vc.vim_d2) to queue up the
follow-on handler. Otherwise, the handler calls vc.quit() to end the
command.</p>
</section>
<section id="vim-d2">
<h3><a class="toc-backref" href="#id5">vim_d2</a><a class="headerlink" href="#vim-d2" title="Permalink to this heading">¶</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">vim_d2</span><span class="p">(</span><span class="n">vc</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">is_text_widget</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">w</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">stroke</span> <span class="o">==</span> <span class="s1">&#39;d&#39;</span><span class="p">:</span>
            <span class="n">w</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">w</span>
            <span class="n">i</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getInsertPoint</span><span class="p">()</span>
            <span class="k">for</span> <span class="n">z</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">n1</span><span class="o">*</span><span class="n">vc</span><span class="o">.</span><span class="n">n</span><span class="p">):</span>
                <span class="c1"># It&#39;s simplest just to get the text again.</span>
                <span class="n">s</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getAllText</span><span class="p">()</span>
                <span class="n">i</span><span class="p">,</span><span class="n">j</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">getLine</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="n">i</span><span class="p">)</span>
                <span class="c1"># Special case for end of buffer only for n == 1.</span>
                <span class="c1"># This is exactly how vim works.</span>
                <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">n1</span><span class="o">*</span><span class="n">vc</span><span class="o">.</span><span class="n">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">i</span> <span class="o">==</span> <span class="n">j</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
                    <span class="n">i</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
                <span class="n">w</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">)</span>
            <span class="n">vc</span><span class="o">.</span><span class="n">done</span><span class="p">()</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">vc</span><span class="o">.</span><span class="n">d_stroke</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">stroke</span> <span class="c1"># A scratch var.</span>
            <span class="n">vc</span><span class="o">.</span><span class="n">begin_motion</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">vim_d3</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
</pre></div>
</div>
<p>This is the follow-on handler for the ‘d’ command. It will be called when
the user types a <em>second</em> character following the ‘d’ command in normal
mode.</p>
<p>All forms of the ‘d’ command alter text, so this handler calls vc.quit if
vc.w is not a text widget.</p>
<p>If the second character is another ‘d’, we have the ‘dd’ command. The code
uses the high-level interface to delete a line, then calls vc.done() to end
the command.</p>
<p>If the second character is <em>not</em> a ‘d’, it should be a following motion,
such as “2j” in “d2j”.</p>
<p>vc.vim_d2 remembers the character that started the motion in a <strong>scratch
ivar</strong>, vc.d_stroke. Such ivars are not inited or touched outside of vim_d
and its follow-on key handlers. This code must remember this character so
that the vim_d3 handler will know whether to expand the deleted text to a
line.</p>
<p>Finally, vc.vim_d2 calls vc.begin_motion, which does the following:</p>
<ul class="simple">
<li><p>Calls vc.ignore if the second character doesn’t really start a motion.</p></li>
<li><p>Sets vc.handler to vc.do_inner_motion.  This handles the motion.</p></li>
<li><p>Sets the vc.after_motion to the next follow-on handler: vc.vim_d3.
vc.vim_d3 will be called when the motion is complete. The details are
complicated, but happily the key handlers don’t have to know about them!</p></li>
</ul>
</section>
<section id="vim-d3">
<h3><a class="toc-backref" href="#id6">vim_d3</a><a class="headerlink" href="#vim-d3" title="Permalink to this heading">¶</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">vim_d3</span><span class="p">(</span><span class="n">vc</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&#39;&#39;&#39;Complete the d command after the cursor has moved.&#39;&#39;&#39;</span>
    <span class="c1"># d2w doesn&#39;t extend to line.  d2j does.</span>
    <span class="n">trace</span> <span class="o">=</span> <span class="kc">False</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">g</span><span class="o">.</span><span class="n">unitTesting</span>
    <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">is_text_widget</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">w</span><span class="p">):</span>
        <span class="n">extend_to_line</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">d_stroke</span> <span class="ow">in</span> <span class="p">(</span><span class="s1">&#39;jk&#39;</span><span class="p">)</span>
        <span class="n">w</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">w</span>
        <span class="n">s</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getAllText</span><span class="p">()</span>
        <span class="n">i1</span><span class="p">,</span><span class="n">i2</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">motion_i</span><span class="p">,</span><span class="n">w</span><span class="o">.</span><span class="n">getInsertPoint</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">i1</span> <span class="o">==</span> <span class="n">i2</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">trace</span><span class="p">:</span> <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;no change&#39;</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">i1</span> <span class="o">&lt;</span> <span class="n">i2</span><span class="p">:</span>
            <span class="k">for</span> <span class="n">z</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">n1</span><span class="o">*</span><span class="n">vc</span><span class="o">.</span><span class="n">n</span><span class="p">):</span>
                <span class="k">if</span> <span class="n">extend_to_line</span><span class="p">:</span>
                    <span class="n">i2</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">to_eol</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="n">i2</span><span class="p">)</span>
                    <span class="k">if</span> <span class="n">i2</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="ow">and</span> <span class="n">s</span><span class="p">[</span><span class="n">i2</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">:</span>
                        <span class="n">i2</span> <span class="o">+=</span> <span class="mi">1</span>
                    <span class="k">if</span> <span class="n">trace</span><span class="p">:</span> <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;extend i2 to eol&#39;</span><span class="p">,</span><span class="n">i1</span><span class="p">,</span><span class="n">i2</span><span class="p">)</span>
            <span class="n">w</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">i1</span><span class="p">,</span><span class="n">i2</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span> <span class="c1"># i1 &gt; i2</span>
            <span class="n">i1</span><span class="p">,</span><span class="n">i2</span> <span class="o">=</span> <span class="n">i2</span><span class="p">,</span><span class="n">i1</span>
            <span class="k">for</span> <span class="n">z</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">vc</span><span class="o">.</span><span class="n">n1</span><span class="o">*</span><span class="n">vc</span><span class="o">.</span><span class="n">n</span><span class="p">):</span>
                <span class="k">if</span> <span class="n">extend_to_line</span><span class="p">:</span>
                    <span class="n">i1</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">to_bol</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="n">i1</span><span class="p">)</span>
                    <span class="k">if</span> <span class="n">trace</span><span class="p">:</span> <span class="n">g</span><span class="o">.</span><span class="n">trace</span><span class="p">(</span><span class="s1">&#39;extend i1 to bol&#39;</span><span class="p">,</span><span class="n">i1</span><span class="p">,</span><span class="n">i2</span><span class="p">)</span>
            <span class="n">w</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">i1</span><span class="p">,</span><span class="n">i2</span><span class="p">)</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">done</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
</pre></div>
</div>
<p>This is the second and last follow-on handler for the d command. The
dispatcher that handles vim motions will call this handler after the
motions <strong>have actually happened</strong>.</p>
<p>First, the code double-checks that we are still in a text widget, calling
vc.quit() if not.</p>
<p>Next, the code compares the present insertion point, w,getInsertPoint(),
with the insertion point before the motion happened, vc.motion_i. It
extends the selection range if the scratch ivar, vc.d_stroke, is in (‘jk’).
The code then deletes the selected text.</p>
<p>Finally, this method calls vc.done().</p>
</section>
<section id="vis-d">
<h3><a class="toc-backref" href="#id7">vis_d</a><a class="headerlink" href="#vis-d" title="Permalink to this heading">¶</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">vis_d</span><span class="p">(</span><span class="n">vc</span><span class="p">):</span>
<span class="w">    </span><span class="sd">&#39;&#39;&#39;Delete the highlighted text and terminate visual mode.&#39;&#39;&#39;</span>
    <span class="n">w</span>  <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">vis_mode_w</span>
    <span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">is_text_widget</span><span class="p">(</span><span class="n">w</span><span class="p">):</span>
        <span class="n">i1</span> <span class="o">=</span> <span class="n">vc</span><span class="o">.</span><span class="n">vis_mode_i</span>
        <span class="n">i2</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">getInsertPoint</span><span class="p">()</span>
        <span class="n">w</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">i1</span><span class="p">,</span><span class="n">i2</span><span class="p">)</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="s1">&#39;normal&#39;</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">done</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">vc</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
</pre></div>
</div>
<p>This is the key handler for the ‘d’ key in normal mode.</p>
<p>It is <em>not</em> a follow-on method of vim_d. The dispatcher calls this method
after visual mode has highlighted text. Here is the entry for ‘d’ in
vc.visual_dispatch_d: ‘d’:vc.vis_d.</p>
<p>Visual mode has already highlighted the text to be deleted, so this code
simply deletes the highlighted text and calls vc.done().</p>
</section>
</section>
<section id="code-level-details">
<h2><a class="toc-backref" href="#id8">Code level details</a><a class="headerlink" href="#code-level-details" title="Permalink to this heading">¶</a></h2>
<p>The VimCommands class in leoVim.py implements Leo’s vim mode. Vim mode is active only if &#64;bool vim-mode = True.</p>
<p>The following sections will be of interest only to those seeking a deep knowledge of how vim mode’s dispatchers work. Such knowledge should rarely be required because dispatchers and key handlers are completely unaware of each other. Dispatch dicts and acceptance methods shield dispatchers and key handlers of all knowledge of each other. In particular, acceptance methods handle the sometimes tricky details of ending a key handler.</p>
<p>Leo’s vim code is spectacularly different from the real vim’s code. Wherever possible, Leo uses methods to hide implementation details.</p>
<p>Ironically, now that everything is hard coded in tables, it would be easy for plugins to customize the workings of vim-mode.</p>
<section id="initialization">
<h3><a class="toc-backref" href="#id9">Initialization</a><a class="headerlink" href="#initialization" title="Permalink to this heading">¶</a></h3>
<p>The init code for each Leo commander c assigns an instance of VimCommands to c.vimCommands. This is done regardless of the &#64;bool vim-mode setting.</p>
<p>Each ivar of the VimCommands class is inited by exactly one of the following:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">vc</span><span class="o">.</span><span class="n">init_constant_ivars</span><span class="p">()</span>
<span class="n">vc</span><span class="o">.</span><span class="n">init_dot_ivars</span><span class="p">()</span>
<span class="n">vc</span><span class="o">.</span><span class="n">init_persistent_ivars</span><span class="p">()</span>
<span class="n">vc</span><span class="o">.</span><span class="n">init_state_ivars</span><span class="p">()</span>
<span class="n">vc</span><span class="o">.</span><span class="n">create_dispatch_dicts</span><span class="p">()</span>
</pre></div>
</div>
<p>In effect, this code partitions each ivar into disjoint sets. This partitioning simplifies code that must re-init some ivars but not others.</p>
<p>The init code creates <strong>dispatch dicts</strong> used by dispatchers.</p>
</section>
<section id="dispatchers">
<h3><a class="toc-backref" href="#id10">Dispatchers</a><a class="headerlink" href="#dispatchers" title="Permalink to this heading">¶</a></h3>
<p>Depending on various state date, dispatchers route incoming keys to the proper <strong>key handler</strong>. Dispatchers use <strong>dispatch dicts</strong> to assign handlers to incoming keys. These dicts eliminate almost all special case code.</p>
<p>vc.do_key is the top-level dispatcher. k.masterKeyHandler calls it for all keys <em>except</em> Ctrl-G. <strong>Note</strong>: k.masterKeyHandler calls vc.do_key only when there no key state in effect, that is, when the minibuffer is not active.</p>
<p>As discussed below, the value returned by vc.do_key tells k.masterKeyHandler whether vim mode has completely handled the key.</p>
<p>Depending on the vc.handler ivar, vc.do_key can route the incoming key either to an <strong>inner dispatcher</strong> or directly to a key handler.</p>
<p>Inner dispatchers handle keys for a particular vim mode using dispatch dicts. Inner dispatchers the following ivars behind the scenes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">vc</span><span class="o">.</span><span class="n">handler</span><span class="p">,</span> <span class="n">vc</span><span class="o">.</span><span class="n">next_func</span><span class="p">,</span> <span class="n">vc</span><span class="o">.</span><span class="n">return_value</span>
<span class="n">vc</span><span class="o">.</span><span class="n">in_motion</span> <span class="ow">and</span> <span class="n">vc</span><span class="o">.</span><span class="n">motion_func</span>
</pre></div>
</div>
<p>Handling these ivars can be tricky; hiding the details greatly simplifies all key handlers.</p>
</section>
<section id="about-key-handlers">
<h3><a class="toc-backref" href="#id11">About key handlers</a><a class="headerlink" href="#about-key-handlers" title="Permalink to this heading">¶</a></h3>
<p>Key handlers handle a single key during the parsing of a vim command. Key handlers can either complete a command, thereby actually doing something, or change state so as to be able to parse (and possibly complete) the next incoming keystroke.</p>
<p>For example, the key handler for the G command handles the command completely. In contrast, two key handlers are needed to handle the gg command. The first handler, vc.vim_g, simply calls vc.accept(handler=vc.vim_g2). This call changes the vc.handler ivar to point to the <strong>follow-on handler</strong>, vim_g2. vim_g2 handles all commands after the user has typed ‘g’ in normal mode.</p>
<p>Each key handler must end with a call to an <strong>acceptance method</strong>. vc.accept is one such method. Acceptance methods prepare for the next keystroke by setting internal state ivars used by the various dispatchers.</p>
<p>Many key handlers simply call vc.done(). This method handles all the details of completing a key handler: it hides the details of parsing vim command.</p>
<p><strong>Important</strong>: Any key handler that wants to change vc.state should set vc.state <em>before</em> calling vc.done()</p>
<p>Key handlers can call either <strong>direct acceptance methods</strong>, vc.accept, vc.delegate, vc.done, vc.ignore, vc.not_ready, vc.quit, and vc.reset, or <strong>indirect acceptance methods</strong>: vc.begin_insert_mode, vc.begin_motion, vc.end_insert_mode, and vc.vim_digits. Indirect acceptance methods must eventually call direct acceptance methods.</p>
</section>
<section id="ivars-for-key-handlers">
<h3><a class="toc-backref" href="#id12">Ivars for key handlers</a><a class="headerlink" href="#ivars-for-key-handlers" title="Permalink to this heading">¶</a></h3>
<p>Dispatchers set the following ivars for each key handler:</p>
<p><strong>vc.w</strong> is the widget that has focus. Key handlers may use convenience methods to determine the location and type of vc.w. The most important are:</p>
<ul class="simple">
<li><p>vc.is_text_widget(w): True if w is any text widget, including headlines, body text and log pane.</p></li>
<li><p>vc.in_headline(w): True if w is a headline widget in edit mode.</p></li>
</ul>
<p><strong>vc.stroke</strong> is a standard Leo stroke representing the incoming key. Note that the spelling of the stoke using the Tk spellings. Take a look at entries in the dispatch dicts to see such spellings. When in doubt, enable the trace in vc.do_key to see the incoming strokes.</p>
<p><strong>vc.n1</strong> and <strong>vc.n</strong> are the repeat counts in effect for each key handler. Dispatchers and their allies handle most details of setting these repeat counts, so most key handlers can simply use vc.n1*vc.n as the ultimate repeat count.</p>
<p><strong>vc.motion_i</strong> is the insertion point <em>before</em> the motion has taken place.</p>
</section>
<section id="handling-tabs">
<h3><a class="toc-backref" href="#id13">Handling tabs</a><a class="headerlink" href="#handling-tabs" title="Permalink to this heading">¶</a></h3>
<p>Various vim commands advertise, just by having a tab_callback method, that they want to handle a tab that follows their name. ga.do_tab then defers to the vim command. Vim’s tab handler no longer knows <em>anything</em> about colon commands, or what any command intends to do with the tab. If the command handler has a tab_callback attribute, vim’s tab handler just calls it.</p>
<p>Here is the flattened form of the class that handles the :tabnew command. Note that the __call__ and tab_callback methods are trivial:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Tabnew</span><span class="p">:</span>
<span class="w">    </span><span class="sd">&#39;&#39;&#39;</span>
<span class="sd">    A class to handle Vim&#39;s :tabnew command.</span>
<span class="sd">    This class supports the do_tab callback.</span>
<span class="sd">    &#39;&#39;&#39;</span>
    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">vc</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&#39;&#39;&#39;Ctor for VimCommands.tabnew class.&#39;&#39;&#39;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">vc</span> <span class="o">=</span> <span class="n">vc</span>
    <span class="vm">__name__</span> <span class="o">=</span> <span class="s1">&#39;:tabnew&#39;</span>
        <span class="c1"># Required.</span>

    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">event</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&#39;&#39;&#39;Prompt for a file name, the open a new Leo tab.&#39;&#39;&#39;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">vc</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">k</span><span class="o">.</span><span class="n">getFileName</span><span class="p">(</span><span class="n">event</span><span class="p">,</span><span class="n">callback</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">open_file_by_name</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">tab_callback</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w">        </span><span class="sd">&#39;&#39;&#39;Called when the user types :tabnew&lt;tab&gt;&#39;&#39;&#39;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">vc</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">k</span><span class="o">.</span><span class="n">getFileName</span><span class="p">(</span><span class="n">event</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span><span class="n">callback</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">open_file_by_name</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">open_file_by_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">fn</span><span class="p">):</span>
        <span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">vc</span><span class="o">.</span><span class="n">c</span>
        <span class="k">if</span> <span class="n">fn</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">g</span><span class="o">.</span><span class="n">os_path_isdir</span><span class="p">(</span><span class="n">fn</span><span class="p">):</span>
            <span class="n">c2</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">openWithFileName</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span><span class="n">old_c</span><span class="o">=</span><span class="n">c</span><span class="p">)</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">g</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">gui</span><span class="o">.</span><span class="n">runAtIdle</span><span class="p">(</span><span class="n">c2</span><span class="o">.</span><span class="n">treeWantsFocusNow</span><span class="p">)</span>
            <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
                <span class="k">pass</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">c</span><span class="o">.</span><span class="n">new</span><span class="p">()</span>
</pre></div>
</div>
<p>This pattern is particularly well suited to Leo, because the various getPublicCommands methods reference those functions in their command dictionaries. Here, the new entries are:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="s1">&#39;:r&#39;</span><span class="p">:</span>       <span class="n">vc</span><span class="o">.</span><span class="n">LoadFileAtCursor</span><span class="p">(</span><span class="n">vc</span><span class="p">),</span>
<span class="s1">&#39;:tabnew&#39;</span><span class="p">:</span>  <span class="n">vc</span><span class="o">.</span><span class="n">Tabnew</span><span class="p">(</span><span class="n">vc</span><span class="p">),</span>
</pre></div>
</div>
</section>
<section id="api-s-for-key-handlers">
<h3><a class="toc-backref" href="#id14">API’s for key handlers</a><a class="headerlink" href="#api-s-for-key-handlers" title="Permalink to this heading">¶</a></h3>
<p>The simplest way of moving the cursor or changing text is to use the vc.do method, a thin wrapper for c.k.simulateCommand.  For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">vc</span><span class="o">.</span><span class="n">state</span> <span class="o">==</span> <span class="s1">&#39;visual&#39;</span><span class="p">:</span>
    <span class="n">vc</span><span class="o">.</span><span class="n">do</span><span class="p">(</span><span class="s1">&#39;end-of-buffer-extend-selection&#39;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">vc</span><span class="o">.</span><span class="n">do</span><span class="p">(</span><span class="s1">&#39;end-of-buffer&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>Key handlers may also use the <strong>high-level interface</strong>. This is the API used throughout Leo’s core. For details, see the HighLevelInterface class in leoFrame.py and various subclasses in qtGui.py.</p>
</section>
<section id="vc-return-value-and-internal-error-checking">
<h3><a class="toc-backref" href="#id15">vc.return_value and internal error checking</a><a class="headerlink" href="#vc-return-value-and-internal-error-checking" title="Permalink to this heading">¶</a></h3>
<p>vc.do_key returns the value of vc.return_value. Most the acceptance functions set vc.return_value to True, indicating that vim mode has completely handled the key and that k.masterKeyHandler should simply return. k.masterKeyHandler handles the key as usual if vc.do_key returns False.</p>
<p>Each key handler sets vc.return_value indirectly by calling an acceptance method. A simple check in vc.do_key ensures that every key handler, has, in fact, called an acceptance method. In practice, this check has been very effective.</p>
</section>
</section>
</section>


            <div class="clearer"></div>
          </div>
        </div>
      </div>
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
            <p class="logo"><a href="leo_toc.html">
              <img class="logo" src="_static/LeoLogo.svg" alt="Logo"/>
            </a></p>
  <div>
    <h4>Previous topic</h4>
    <p class="topless"><a href="theory.html"
                          title="previous chapter">Exploring Leo’s Code Base</a></p>
  </div>
  <div>
    <h4>Next topic</h4>
    <p class="topless"><a href="leonine-world.html"
                          title="next chapter">The Leonine World</a></p>
  </div>
<div id="searchbox" style="display: none" role="search">
  <h3 id="searchlabel">Quick search</h3>
    <div class="searchformwrapper">
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
      <input type="submit" value="Go" />
    </form>
    </div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
        </div>
<div id="sidebarbutton" title="Collapse sidebar">
<span>«</span>
</div>

      </div>
      <div class="clearer"></div>
    </div>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="leonine-world.html" title="The Leonine World"
             >next</a> |</li>
        <li class="right" >
          <a href="theory.html" title="Exploring Leo’s Code Base"
             >previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" >Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">Vim Mode: Theory of Operation</a></li> 
      </ul>
    </div>
    <div class="footer" role="contentinfo">
        &#169; Copyright 1997-2023, Edward K. Ream.
      Last updated on February 28, 2023.
      Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 5.3.0.
    </div>
  </body>
</html>